#include "comment.h"
int count = 0;
struct sockaddr_in clientArr[1000]; // 报到数组
LOGIN_INFO loginInfoArr[1000];      // 登录信息数组
int loginCount = 0;                 // 登录的人数，有时候也作为下标用

// 把聊天记录保存到文件中
void saveChatRecordToFile(SINGLE_CHAT *pSc)
{
     int fd = open("chatRecord.dat", O_WRONLY|O_APPEND);
     printf("聊天记录保存,内容是：%s %s %s %ld", pSc->selfName, pSc->destName, pSc->text, pSc->t);
     write(fd, pSc, sizeof(SINGLE_CHAT));
     close(fd);
}

// 查询聊天记录文件里面的聊天记录，并发给客户端
void queryChatRecord(int sID, SINGLE_CHAT *pSc, struct sockaddr_in clientAddr)
{
     int fd = open("chatRecord.dat", O_RDONLY);
     SINGLE_CHAT sc;
     int ret = 0;
     MSG msg;
     // 读取所有聊天记录，并判断是否是指定的两个用户的记录
     while (ret = read(fd, &sc, sizeof(SINGLE_CHAT)))
     {
          // 如果要查询的两个名字和文件中聊天记录的两个名字相同，说明该聊天记录时要查询的
          if ((strcmp(pSc->selfName, sc.selfName) == 0 || strcmp(pSc->destName, sc.selfName) == 0) &&
              (strcmp(pSc->selfName, sc.destName) == 0 || strcmp(pSc->destName, sc.destName) == 0))
          {
               msg.type = queryChatRec; // 消息类型时查询聊天记录
               memcpy(msg.buf, &sc, sizeof(SINGLE_CHAT));
               // 把包含有一条聊天记录的消息发送给客户端
               int ret = sendto(sID, &msg, sizeof(msg), 0, (struct sockaddr *)&clientAddr, sizeof(struct sockaddr_in));
               if (ret == -1)
               {
                    printf("发送数据出错了\n");
                    break;
               }
          }
     }

     msg.type = queryOver; // 查询结束
     //  把查询的消息发送给客户端
     ret = sendto(sID, &msg, sizeof(msg), 0, (struct sockaddr *)&clientAddr, sizeof(struct sockaddr_in));
     if (ret == -1)
     {
          printf("发送数据出错了\n");
     }

     close(fd);
}

int main()
{
     struct sockaddr_in sin;
     sin.sin_family = AF_INET;   // IPV4的网络地址版本
     sin.sin_port = htons(9010); // 把9000号端口转为网络字节序并赋值给端口参数

     u_int32_t ip = 0;
     // 把字符串ip地址转为网络字节序ip
     inet_pton(AF_INET, "192.168.74.129", &ip);
     sin.sin_addr.s_addr = ip;

     // 创建一个udp套接字，参数1指定地址族，参数2指定协议，SOCK_STREAM代表是TCP，SOCK_DGRAM代表UDP
     int sID = socket(AF_INET, SOCK_DGRAM, 0);

     int opt = 1;
     if (setsockopt(sID, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0)
     {
          perror("setsockopt");
     }

     // 把套接字和地址绑定（手机+手机号）
     if (-1 == bind(sID, (struct sockaddr *)&sin, sizeof(sin)))
     {
          perror("绑定地址失败，请等待30秒或者换一个端口试试");
          return 0;
     }

     while (1)
     {
          struct sockaddr_in clientAddr;
          socklen_t len = sizeof(clientAddr);
          MSG msg; // 只收MSG类型的消息
          // clientAddr是对方的地址,len地址的长度
          int ret = recvfrom(sID, &msg, sizeof(msg), 0, (struct sockaddr *)&clientAddr, &len);
          printf("收到一条消息，类型为：%d 内容是：%s 收到字节数为：%d\n", msg.type, msg.buf, ret);
          if (ret == -1)
          {
               printf("接受数据出错了\n");
          }
          if (msg.type == baodao)
          {
               // 保存新报到的用户到数组中，因为UDP发消息是直接使用地址，所以这里保存的是客户端的地址结构
               clientArr[count] = clientAddr;
               count++;

               // 把网络结构体转为自定义网络地址结构体，方便用户查看地址
               HOSTADDR ha = convertNetToHost(clientAddr);
               printf("%s %u\n", ha.ip, ha.port);
          }
          else if (msg.type == quit)
          {
               // 遍历所有的客户端地址
               for (int i = 0; i < count; i++)
               {
                    // 说明下标为i的元素是需要从数组中删除的
                    if (clientAddr.sin_addr.s_addr == clientArr[i].sin_addr.s_addr && clientAddr.sin_port == clientArr[i].sin_port)
                    {
                         // 如果不是最后一个元素
                         if (i != count - 1)
                         {
                              // 把数组后面的元素往前移动
                              for (size_t j = i; j < count - 1; j++)
                              {
                                   clientArr[j] = clientArr[j + 1];
                              }
                         }
                         // 总连接上的客户端数量-1
                         count--;
                         printf("有客户端退出，总连接数变为:%d\n", count);
                    }
               }
          }
          else if (msg.type == regist)
          {
               // 把缓冲区的类型转为注册
               RGU *pRgu = (RGU *)msg.buf;
               // 把收到的用户注册信息存入到文件中
               int fd = open("user.dat", O_CREAT | O_WRONLY | O_APPEND, 0777);
               write(fd, pRgu, sizeof(RGU));
               close(fd);
          }
          else if (msg.type == login)
          {
               // 把缓冲区的类型转为注册，因为注册和登录用的是同一个结构体
               RGU *pRgu = (RGU *)msg.buf;
               // 把收到的用户注册信息存入到文件中
               int fd = open("user.dat", O_RDONLY);
               if (fd == -1)
               {
                    perror("打开用户文件出错");
                    return 0;
               }

               int ret = 0;
               RGU rgu;
               // 因为登录成功了，所以把消息类型改为ok，再发回去
               msg.type = no;
               printf("收到登录消息，用户是：%s 密码是：%s\n", pRgu->name, pRgu->pwd);
               // 读取文件中的用户，用来和网络发来的用户名和密码进行比较
               while (ret = read(fd, &rgu, sizeof(rgu)))
               {
                    printf("当前和%s %s比较\n", rgu.name, rgu.pwd);
                    // 判断用户名和密码是否匹配
                    if (strcmp(rgu.name, pRgu->name) == 0 && strcmp(rgu.pwd, pRgu->pwd) == 0)
                    {
                         printf("比较成功了\n");
                         // 因为登录成功了，所以把消息类型改为ok，再发回去
                         msg.type = ok;
                         // 把登录成功的用户名和地址保存起来
                         strcpy(loginInfoArr[loginCount].name, rgu.name);
                         loginInfoArr[loginCount].addr = clientAddr;
                         loginCount++;
                         break;
                    }
               }
               close(fd);

               printf("消息类型为：%d\n", msg.type);
               // 把消息发回客户端，告诉它登录成功了
               ret = sendto(sID, &msg, sizeof(msg), 0, (struct sockaddr *)&clientAddr, sizeof(clientAddr));
               if (ret == -1)
               {
                    printf("发送数据出错了\n");
                    break;
               }
               close(fd);
          }
          else if (msg.type == singleChat) // 单聊
          {
               SINGLE_CHAT *pSC = (SINGLE_CHAT *)msg.buf;
               printf("收到了%s发来的消息，目标是：%s . 内容是:%s\n", pSC->selfName, pSC->destName, pSC->text);
               // 遍历所有登录的人
               for (size_t i = 0; i < loginCount; i++)
               {
                    // 判断下标为i的用户是不是要私聊的对象
                    if (strcmp(loginInfoArr[i].name, pSC->destName) == 0)
                    {
                         printf("找到了要聊天的对象，现在开始发消息给他\n");
                         pSC->t = time(0); // 记录发消息的时间
                         // 把消息发送给服务器
                         int ret = sendto(sID, &msg, sizeof(msg), 0, (struct sockaddr *)&loginInfoArr[i].addr, sizeof(struct sockaddr_in));
                         if (ret == -1)
                         {
                              printf("发送数据出错了\n");
                              break;
                         }
                         saveChatRecordToFile(pSC);
                    }
               }
          }
          else if (msg.type == queryChatRec)
          {
               SINGLE_CHAT *pSC = (SINGLE_CHAT *)msg.buf;
               queryChatRecord(sID, pSC, clientAddr);
          }
          else
          {
               // 遍历所有的客户端地址
               for (int i = 0; i < count; i++)
               {
                    // 说明下标为i的元素是需要从数组中删除的
                    if (clientAddr.sin_addr.s_addr != clientArr[i].sin_addr.s_addr || clientAddr.sin_port != clientArr[i].sin_port)
                    {
                         // 把消息发送给服务器
                         int ret = sendto(sID, &msg, sizeof(msg), 0, (struct sockaddr *)&clientArr[i], sizeof(clientArr[i]));
                         if (ret == -1)
                         {
                              printf("发送数据出错了\n");
                              break;
                         }
                         else
                         {
                              printf("转发了一条消息%s\n", msg.buf);
                         }
                         // printf("我要把消息打印出来:%s\n", buf);
                    }
               }
          }
     }

     close(sID);
}